﻿using System;
using System.ComponentModel;
using System.Linq;
using System.ComponentModel.Design;
using jr = Jayrock.Json.Conversion;

namespace N2.ComponentModel
{
	using Json.Converters;
	using Newtonsoft.Json;
	using N2.Jayrock.Json.Converters;
	
	/// <summary>
	/// Filter which ContentItem's properties should be serializaed to JSON,
	/// and how it should be done.
	/// </summary>
	class JsonContentItemTypeDescriptor : CustomTypeDescriptor
	{
		#region Fields

		readonly ICustomTypeDescriptor Parent;
		readonly jr.IPropertyDescriptorCustomization customizer = new ContentItemLinkPropertyExporterAttribute();

		#endregion Fields

		#region ctor

		public JsonContentItemTypeDescriptor(ICustomTypeDescriptor parent)
			: base(parent)
		{
			this.Parent = parent;
		}

		#endregion ctor

		#region Methods

		public override PropertyDescriptorCollection GetProperties()
		{
			return this.FilterProperties(this.Parent.GetProperties());
		}

		public override PropertyDescriptorCollection GetProperties(
			Attribute[] attributes)
		{
			return this.FilterProperties(this.Parent.GetProperties(attributes));
		}

		PropertyDescriptorCollection FilterProperties(PropertyDescriptorCollection input)
		{
			return new PropertyDescriptorCollection((
				from _property in input.Cast<PropertyDescriptor>()
				//filter out ignored properties..
				where !"Details,DetailCollections,AuthorizedRoles,RewrittenUrl"
					.Split(',')
					.Contains(
						_property.Name,
						StringComparer.OrdinalIgnoreCase)
				select ProcessProperty(_property)
			).ToArray());
		}

		/// <summary>
		/// Transforms a given property definition by applying a meta-data
		/// that describes some detail on how to serialize a given property.
		/// </summary>
		/// <param name="originalProperty"></param>
		/// <returns></returns>
		PropertyDescriptor ProcessProperty(PropertyDescriptor originalProperty)
		{
			
			if(typeof(ContentItem).IsAssignableFrom(originalProperty.PropertyType)) {

				//if original property was obtained from JayRock's CustomTypeDescriptor
				if (originalProperty is IServiceContainer) {
					this.customizer.Apply(originalProperty);
					return originalProperty;
				} else {
					//otherwise considering Newtonsoft's serializer..
					return TypeDescriptor.CreateProperty(
							originalProperty.ComponentType,
							originalProperty,
							new JsonConverterAttribute(
								typeof(JsonContentItemProxyConverter)
							),
							new ContentItemLinkPropertyExporterAttribute()
					);
				}
			}

			if("Children" == originalProperty.Name) {
				TypeDescriptor.CreateProperty(
					originalProperty.ComponentType,
					originalProperty,
					new JsonConverterAttribute(
						typeof(JsonChildrenCollectionConverter)
					)
				);
			}

			return originalProperty;
		}

		#endregion Methods

		#region Types

		class ContentItemLinkPropertyExporterAttribute : Attribute, jr.IPropertyDescriptorCustomization
		{
			public void Apply(PropertyDescriptor property)
			{
				if (property == null)
					throw new ArgumentNullException("property");

				IServiceContainer container = property as IServiceContainer;

				if (null != container) {
					container.AddService(
						typeof(jr.IObjectMemberExporter),
						new ContentItemLinkExporter(property));
				}
			}
		}

		
		
		#endregion Types
	}
}
